package cn.zhz.privacy.kspapi;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.util.ArrayList;
import java.util.List;

/**
 * 密码服务接口-密码运算类
 */
public class KspCipherApi extends KspApi {
    // --------------------------------------------------对称密码运算类接口----------------------------------------------------------------

    /**
     * SM4对称加密-ECB模式
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 被加密的明文数据
     * @return Base64编码的密文
     * @throws Exception
     */
    public static String sm4Encrypt(String keyId, byte[] plaintext) throws Exception {
        return encrypt(keyId, plaintext, null, null, null);
    }

    /**
     * SM4对称解密-ECB模式
     *
     * @param keyId      密钥唯一标识，可使用密钥别名代替
     * @param ciphertext Base64编码的密文
     * @return 明文数据
     * @throws Exception
     */
    public static byte[] sm4Decrypt(String keyId, String ciphertext) throws Exception {
        return decrypt(keyId, ciphertext, null, null, null);
    }

    /**
     * SM3-HMAC
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 明文数据
     * @return Base64编码的散列值
     * @throws Exception
     */
    public static String sm3Hmac(String keyId, byte[] plaintext) throws Exception {
        return hmac(keyId, plaintext, "SM3");
    }

    /**
     * 对称加密
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 被加密的明文数据
     * @param alg       密码算法，支持的算法有：SM1/SM4/DES/3DES-2KEY/3DES-3KEY/AES-128/AES-192,默认使用SM4
     * @param algMode   工作模式，支持的模式有：ECB/CBC/OFB/CFB, 默认使用ECB。
     * @param iv        初始化向量，非ECB模式必填
     * @return Base64编码的密文
     * @throws Exception
     */
    public static String encrypt(String keyId, byte[] plaintext, String alg, String algMode, byte[] iv) throws Exception {

        if (plaintext == null || plaintext.length == 0) {
            return "";
        }

        String url = "/api/cipher/encrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("plaintext", base64Encode(plaintext));
        params.put("alg", alg);
        params.put("algMode", algMode);
        params.put("iv", base64Encode(iv));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("ciphertext");
    }

    /**
     * 对称解密
     *
     * @param keyId      密钥唯一标识，可使用密钥别名代替
     * @param ciphertext Base64编码的密文
     * @param alg        密码算法，支持的算法有：SM1/SM4/DES/3DES-2KEY/3DES-3KEY/AES-128/AES-192,默认使用SM4
     * @param algMode    工作模式，支持的模式有：ECB/CBC/OFB/CFB, 默认使用ECB。
     * @param iv         初始化向量，非ECB模式必填
     * @return 明文数据
     * @throws Exception
     */
    public static byte[] decrypt(String keyId, String ciphertext, String alg, String algMode, byte[] iv) throws Exception {

        if (StrUtil.isBlank(ciphertext)) {
            return new byte[]{};
        }
        if(!Base64.isBase64(ciphertext)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/decrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("ciphertext", ciphertext);
        params.put("alg", alg);
        params.put("algMode", algMode);
        params.put("iv", base64Encode(iv));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return base64Decode(result.getJSONObject("result").getString("plaintext"));
    }

    /**
     * 批量对称加密
     *
     * @param keyId           密钥唯一标识，可使用密钥别名代替
     * @param plaintextArrays 被加密的明文数据
     * @param alg             密码算法，支持的算法有：SM1/SM4/DES/3DES-2KEY/3DES-3KEY/AES-128/AES-192,默认使用SM4
     * @param algMode         工作模式，支持的模式有：ECB/CBC/OFB/CFB, 默认使用ECB。
     * @param iv              初始化向量，非ECB模式必填
     * @return Base64编码的密文List
     * @throws Exception
     */
    public static List<String> batchEncrypt(String keyId, List<byte[]> plaintextArrays, String alg, String algMode, byte[] iv) throws Exception {
        List<String> base64Arrays = new ArrayList<>();
        for (byte[] plaintext : plaintextArrays) {
            base64Arrays.add(base64Encode(plaintext));
        }
        String url = "/api/cipher/batchEncrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("plaintextArrays", base64Arrays);
        params.put("alg", alg);
        params.put("algMode", algMode);
        params.put("iv", base64Encode(iv));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        JSONArray ciphertextArrays = result.getJSONObject("result").getJSONArray("ciphertextArrays");
        return ciphertextArrays.toJavaList(String.class);
    }

    /**
     * 批量对称解密
     *
     * @param keyId            密钥唯一标识，可使用密钥别名代替
     * @param ciphertextArrays Base64编码的密文List
     * @param alg              密码算法，支持的算法有：SM1/SM4/DES/3DES-2KEY/3DES-3KEY/AES-128/AES-192,默认使用SM4
     * @param algMode          工作模式，支持的模式有：ECB/CBC/OFB/CFB, 默认使用ECB。
     * @param iv               初始化向量，非ECB模式必填
     * @return 明文数据List
     * @throws Exception
     */
    public static List<byte[]> batchDecrypt(String keyId, List<String> ciphertextArrays, String alg, String algMode, byte[] iv) throws Exception {

        for (String ciphertext : ciphertextArrays) {
            if(!Base64.isBase64(ciphertext)){
                throw new RuntimeException("传入数据不是base64编码");
            }
        }

        String url = "/api/cipher/batchDecrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("ciphertextArrays", ciphertextArrays);
        params.put("alg", alg);
        params.put("algMode", algMode);
        params.put("iv", base64Encode(iv));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        JSONArray base64Arrays = result.getJSONObject("result").getJSONArray("plaintextArrays");
        List<byte[]> plaintextArrays = new ArrayList<>();
        for (int i = 0; i < base64Arrays.size(); i++) {
            if (StrUtil.isBlank(base64Arrays.getString(i))) {
                plaintextArrays.add(null);
            } else {
                plaintextArrays.add(base64Decode(base64Arrays.getString(i)));
            }
        }
        return plaintextArrays;
    }


    /**
     * 计算MAC值
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 明文数据
     * @param alg       密码算法，支持的算法有：SM1/SM4/DES/3DES-2KEY/3DES-3KEY/AES-128/AES-192,默认使用SM4
     * @param iv        初始化向量
     * @return Base64编码的散列值
     * @throws Exception
     */
    public static String mac(String keyId, byte[] plaintext, String alg, byte[] iv) throws Exception {

        if (plaintext == null || plaintext.length == 0) {
            return "";
        }

        String url = "/api/cipher/mac";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("plaintext", base64Encode(plaintext));
        params.put("alg", alg);
        params.put("iv", base64Encode(iv));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("hashValue");
    }

    /**
     * 计算HMAC值
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 明文数据
     * @param alg       散列算法，支持的算法有：SHA1/SHA224/SHA256/SHA384/SHA512/SM3
     * @return Base64编码的散列值
     * @throws Exception
     */
    public static String hmac(String keyId, byte[] plaintext, String alg) throws Exception {

        if (plaintext == null || plaintext.length == 0) {
            return "";
        }

        String url = "/api/cipher/hmac";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("plaintext", base64Encode(plaintext));
        params.put("alg", alg);
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("hashValue");
    }

    /**
     * 获取指定长度随机数
     *
     * @param length 随机数长度
     * @return 随机数
     * @throws Exception
     */
    public static byte[] random(int length) throws Exception {
        String url = "/api/cipher/random";
        JSONObject params = new JSONObject();
        params.put("length", length);
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return base64Decode(result.getJSONObject("result").getString("randomData"));
    }

    // --------------------------------------------------非对称密码运算类接口----------------------------------------------------------------

    /**
     * SM2加密
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 明文数据
     * @return Base64编码的密文
     * @throws Exception
     */
    public static String sm2Encrypt(String keyId, byte[] plaintext) throws Exception {


        if (plaintext == null || plaintext.length == 0) {
            return "";
        }

        String url = "/api/cipher/sm2Encrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("plaintext", base64Encode(plaintext));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("ciphertext");
    }

    /**
     * SM2加密（使用外部密钥）
     *
     * @param publicKey 公钥数据
     * @param plaintext 明文数据
     * @return Base64编码的密文
     * @throws Exception
     */
    public static String sm2Encrypt4External(byte[] publicKey, byte[] plaintext) throws Exception {

        if (plaintext == null || plaintext.length == 0) {
            return "";
        }

        String url = "/api/cipher/sm2Encrypt";
        JSONObject params = new JSONObject();
        params.put("publicKey", base64Encode(publicKey));
        params.put("plaintext", base64Encode(plaintext));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("ciphertext");
    }

    /**
     * SM2解密
     *
     * @param keyId      密钥唯一标识，可使用密钥别名代替
     * @param ciphertext Base64编码的密文
     * @return 明文数据
     * @throws Exception
     */
    public static byte[] sm2Decrypt(String keyId, String ciphertext) throws Exception {

        if (StrUtil.isBlank(ciphertext)) {
            return new byte[]{};
        }

        if(!Base64.isBase64(ciphertext)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/sm2Decrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("ciphertext", ciphertext);
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return base64Decode(result.getJSONObject("result").getString("plaintext"));
    }

    /**
     * SM2解密（使用外部密钥）
     *
     * @param privateKey 私钥数据
     * @param ciphertext Base64编码的密文
     * @return 明文数据
     * @throws Exception
     */
    public static byte[] sm2Decrypt4External(byte[] privateKey, String ciphertext) throws Exception {

        if (StrUtil.isBlank(ciphertext)) {
            return new byte[]{};
        }
        if(!Base64.isBase64(ciphertext)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/sm2Decrypt";
        JSONObject params = new JSONObject();
        params.put("privateKey", base64Encode(privateKey));
        params.put("ciphertext", ciphertext);
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return base64Decode(result.getJSONObject("result").getString("plaintext"));
    }

    /**
     * SM2签名（消息原文）
     *
     * @param keyId   密钥唯一标识，可使用密钥别名代替
     * @param message 消息原文
     * @return Base64编码的签名值
     * @throws Exception
     */
    public static String sm2Sign(String keyId, byte[] message) throws Exception {

        if (message == null || message.length == 0) {
            return "";
        }

        String url = "/api/cipher/sm2Sign";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("message", base64Encode(message));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("signature");
    }

    /**
     * SM2签名 （消息摘要）
     *
     * @param keyId         密钥唯一标识，可使用密钥别名代替
     * @param messageDigest 消息摘要
     * @return Base64编码的签名值
     * @throws Exception
     */
    public static String sm2Sign4Digest(String keyId, byte[] messageDigest) throws Exception {

        if (messageDigest == null || messageDigest.length == 0) {
            return "";
        }

        String url = "/api/cipher/sm2Sign";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("message", base64Encode(messageDigest));
        params.put("messageType", "digest");
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("signature");
    }

    /**
     * SM2验签（消息原文）
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param message   消息原文
     * @param signature Base64编码的签名值
     * @return 验签结果
     * @throws Exception
     */
    public static Boolean sm2Verify(String keyId, byte[] message, String signature) throws Exception {

        if (message == null || message.length == 0) {
            return false;
        }
        if (StrUtil.isBlank(signature)) {
            return false;
        }

        if(!Base64.isBase64(signature)){
            throw new RuntimeException("传入数据不是base64编码");
        }


        String url = "/api/cipher/sm2Verify";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("message", base64Encode(message));
        params.put("signature", signature);
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getBoolean("verifyResult");
    }

    /**
     * SM2验签（消息摘要）
     *
     * @param keyId         密钥唯一标识，可使用密钥别名代替
     * @param messageDigest 消息摘要
     * @param signature     Base64编码的签名值
     * @return 验签结果
     * @throws Exception
     */
    public static Boolean sm2Verify4Digest(String keyId, byte[] messageDigest, String signature) throws Exception {

        if (messageDigest == null || messageDigest.length == 0) {
            return false;
        }

        if (StrUtil.isBlank(signature)) {
            return false;
        }
        if(!Base64.isBase64(signature)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/sm2Verify";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("message", base64Encode(messageDigest));
        params.put("signature", signature);
        params.put("messageType", "digest");
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getBoolean("verifyResult");
    }

    /**
     * RSA加密
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param plaintext 明文数据
     * @return Base64编码的密文
     * @throws Exception
     */
    public static String rsaEncrypt(String keyId, byte[] plaintext) throws Exception {

        if (plaintext == null || plaintext.length == 0) {
            return "";
        }

        String url = "/api/cipher/rsaEncrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("plaintext", base64Encode(plaintext));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("ciphertext");
    }

    /**
     * RSA解密
     *
     * @param keyId      密钥唯一标识，可使用密钥别名代替
     * @param ciphertext Base64编码的密文
     * @return 明文数据
     * @throws Exception
     */
    public static byte[] rsaDecrypt(String keyId, String ciphertext) throws Exception {

        if (StrUtil.isBlank(ciphertext)) {
            return new byte[]{};
        }

        if(!Base64.isBase64(ciphertext)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/rsaDecrypt";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("ciphertext", ciphertext);
        JSONObject result = doHttpPost(url, params);
        return base64Decode(result.getJSONObject("result").getString("plaintext"));
    }

    /**
     * RSA签名
     *
     * @param keyId   密钥唯一标识，可使用密钥别名代替
     * @param message 消息原文
     * @return Base64编码的签名值
     * @throws Exception
     */
    public static String rsaSign(String keyId, byte[] message) throws Exception {

        if (message == null || message.length == 0) {
            return "";
        }
        if(!Base64.isBase64(message)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/rsaSign";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("message", base64Encode(message));
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getString("signature");
    }

    /**
     * RSA验签
     *
     * @param keyId     密钥唯一标识，可使用密钥别名代替
     * @param message   消息原文
     * @param signature Base64编码的签名值
     * @return 验签结果
     * @throws Exception
     */
    public static Boolean rsaVerify(String keyId, byte[] message, String signature) throws Exception {

        if (message == null || message.length == 0) {
            return false;
        }

        if (StrUtil.isBlank(signature)) {
            return false;
        }
        if(!Base64.isBase64(signature)){
            throw new RuntimeException("传入数据不是base64编码");
        }

        String url = "/api/cipher/rsaVerify";
        JSONObject params = new JSONObject();
        params.put("keyId", keyId);
        params.put("message", base64Encode(message));
        params.put("signature", signature);
        JSONObject result = doHttpPost(url, params);
        if (result.getInteger("status") != 200) {
            throw new RuntimeException(result.getString("message"));
        }
        return result.getJSONObject("result").getBoolean("verifyResult");
    }

}
